from __future__ import annotations

from dataclasses import dataclass
from enum import Enum, auto
from secrets import choice
from string import digits
from typing import Iterable

import structlog

from app.core.config import settings
from app.core.redis_client import get_redis
from app.services.notification_service import notification_service

logger = structlog.get_logger('love_notes.notification.manager')


class NotificationType(Enum):
  ACCOUNT_VERIFICATION = auto()
  DIARY_CREATED = auto()
  ANNIVERSARY_CREATED = auto()
  ANNIVERSARY_REMINDER = auto()
  PASSWORD_RESET = auto()


@dataclass
class NotificationEvent:
  type: NotificationType
  recipients: Iterable[str]
  context: dict


class NotificationManager:
  def __init__(self) -> None:
    self._builders = {
      NotificationType.ACCOUNT_VERIFICATION: self._build_verification_email,
      NotificationType.DIARY_CREATED: self._build_diary_email,
      NotificationType.ANNIVERSARY_CREATED: self._build_anniversary_email,
      NotificationType.ANNIVERSARY_REMINDER: self._build_anniversary_reminder_email,
      NotificationType.PASSWORD_RESET: self._build_password_reset_email,
    }

  async def dispatch(self, event: NotificationEvent) -> bool:
    recipients = [r for r in event.recipients if r]
    if not recipients:
      logger.info('Notification skipped: empty recipients', event=event.type.name)
      return False

    builder = self._builders.get(event.type)
    if not builder:
      logger.warning('No notification builder registered', event=event.type.name)
      return False

    subject, body, html = builder(event.context)
    if not subject or not body:
      logger.warning('Notification builder returned empty payload', event=event.type.name)
      return False

    for email in recipients:
      await notification_service.send_email(to=email, subject=subject, body=body, html_body=html)
    return True

  async def send_email_verification(self, *, email: str, username: str) -> str | None:
    code = ''.join(choice(digits) for _ in range(6))
    redis = get_redis()
    if redis:
      key = f'auth:verify:{email}'
      await redis.set(key, code, ex=600)
    else:
      logger.warning('Redis unavailable; verification code cannot be stored', email=email)

    await self.dispatch(
      NotificationEvent(
        type=NotificationType.ACCOUNT_VERIFICATION,
        recipients=[email],
        context={'username': username, 'code': code},
      )
    )
    return code

  async def notify_diary_created(
    self,
    *,
    partner_email: str | None,
    author_name: str,
    title: str,
    summary: str | None = None,
    link: str | None = None,
  ) -> None:
    if not partner_email:
      logger.info('Skip diary notification: partner email missing')
      return
    await self.dispatch(
      NotificationEvent(
        type=NotificationType.DIARY_CREATED,
        recipients=[partner_email],
        context={'author': author_name, 'title': title, 'summary': summary, 'link': link},
      )
    )

  async def notify_anniversary_created(
    self,
    *,
    recipient_emails: Iterable[str],
    creator_name: str,
    anniversary_name: str,
    anniversary_date: str,
  ) -> None:
    await self.dispatch(
      NotificationEvent(
        type=NotificationType.ANNIVERSARY_CREATED,
        recipients=list(recipient_emails),
        context={'creator': creator_name, 'name': anniversary_name, 'date': anniversary_date},
      )
    )

  async def notify_anniversary_reminder(
    self,
    *,
    recipient_emails: Iterable[str],
    anniversary_name: str,
    occurrence_date: str,
    creator_name: str,
  ) -> None:
    await self.dispatch(
      NotificationEvent(
        type=NotificationType.ANNIVERSARY_REMINDER,
        recipients=list(recipient_emails),
        context={'creator': creator_name, 'name': anniversary_name, 'date': occurrence_date},
      )
    )

  async def send_password_reset(self, *, email: str, token: str) -> None:
    base = settings.site_base_url or '#'
    reset_link = f"{base}/reset-password?token={token}"
    await self.dispatch(
      NotificationEvent(
        type=NotificationType.PASSWORD_RESET,
        recipients=[email],
        context={'reset_link': reset_link},
      )
    )

  def _build_verification_email(self, context: dict) -> tuple[str, str, str]:
    code = context.get('code')
    username = context.get('username', '用户')
    subject = f'{settings.app_name} - 邮箱验证'
    body = '\n'.join(
      [
        f'{username}，欢迎加入 {settings.app_name}！',
        '',
        f'你的验证码是：{code}',
        '验证码有效期 10 分钟，请勿泄露给他人。',
      ]
    )
    base = settings.site_base_url or '#'
    html = f'''
    <div style="font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif; color:#0f172a;">
      <h2 style="margin:0 0 12px 0; font-size:18px;">{username}，欢迎加入 {settings.app_name}</h2>
      <p style="margin:0 0 8px 0; color:#475569;">你的验证码：</p>
      <div style="margin:12px 0; padding:12px 16px; font-size:20px; font-weight:700; letter-spacing:0.2em; background:#f8fafc; border:1px solid #e2e8f0; border-radius:12px; display:inline-block;">{code}</div>
      <p style="margin:12px 0 16px 0; color:#64748b;">验证码有效期 10 分钟，请勿泄露给他人。</p>
      <a href="{base}" style="display:inline-block; padding:10px 16px; background:#ef4444; color:#fff; text-decoration:none; border-radius:10px;">进入 {settings.app_name}</a>
    </div>
    '''
    return subject, body, html

  def _build_diary_email(self, context: dict) -> tuple[str, str, str]:
    author = context.get('author', '你的另一半')
    title = context.get('title', '新日记')
    summary = context.get('summary')
    link = context.get('link')
    subject = f'{author} 刚写下了新的日记'
    lines = [
      f'亲爱的，这是来自 {author} 的新日记提醒：',
      f'标题：{title}',
    ]
    if summary:
      lines.append('')
      lines.append('AI 摘要：')
      lines.append(summary)
    if link:
      lines.append('')
      lines.append(f'👉 立即查看：{link}')
    lines.append('')
    lines.append('如需关闭提醒，可在设置中调整邮件通知。')
    body = '\n'.join(lines)
    base = settings.site_base_url or '#'
    to_link = link or base
    html = f'''
    <div style="font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif; color:#0f172a;">
      <div style="height:6px; background:linear-gradient(90deg,#ec4899,#8b5cf6,#06b6d4);"></div>
      <h2 style="margin:16px 0 8px 0; font-size:18px;">来自 {author} 的新日记</h2>
      <p style="margin:0 0 8px 0; color:#475569;">标题：<strong>{title}</strong></p>
      {f'<p style="margin:8px 0 12px 0; color:#334155;">{summary}</p>' if summary else ''}
      <a href="{to_link}" style="display:inline-block; padding:10px 16px; background:#0ea5e9; color:#fff; text-decoration:none; border-radius:10px;">立即查看</a>
      <p style="margin:16px 0 0 0; color:#94a3b8; font-size:12px;">若不想再收到此类邮件，可在设置里关闭。</p>
    </div>
    '''
    return subject, body, html

  def _build_anniversary_email(self, context: dict) -> tuple[str, str, str]:
    creator = context.get('creator', '你的伴侣')
    name = context.get('name', '新的纪念日')
    date = context.get('date', '未知日期')
    subject = f'{creator} 为你们添加了「{name}」纪念日'
    lines = [
      f'{creator} 为你们的爱情日历新增了纪念日：{name}',
      f'日期：{date}',
      '',
      '建议提前安排惊喜或仪式，让这一天留下更特别的记忆。',
    ]
    body = '\n'.join(lines)
    base = settings.site_base_url or '#'
    html = f'''
    <div style="font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif; color:#0f172a;">
      <div style="height:6px; background:linear-gradient(90deg,#ef4444,#fb923c,#f59e0b);"></div>
      <h2 style="margin:16px 0 8px 0; font-size:18px;">新增纪念日：{name}</h2>
      <p style="margin:0 0 6px 0; color:#475569;">创建者：{creator}</p>
      <p style="margin:0 0 12px 0; color:#475569;">日期：{date}</p>
      <a href="{base}" style="display:inline-block; padding:10px 16px; background:#ef4444; color:#fff; text-decoration:none; border-radius:10px;">打开应用</a>
    </div>
    '''
    return subject, body, html

  def _build_anniversary_reminder_email(self, context: dict) -> tuple[str, str, str]:
    creator = context.get('creator', '你的伴侣')
    name = context.get('name', '纪念日')
    date = context.get('date', '即将到来')
    subject = f'提醒：{name} 即将到来'
    lines = [
      f'{creator} 设定的纪念日「{name}」即将到来。',
      f'正式日期：{date}',
      '',
      '抽个时间计划一个小惊喜或留言，让仪式感留在日常里。',
    ]
    body = '\n'.join(lines)
    base = settings.site_base_url or '#'
    html = f'''
    <div style="font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif; color:#0f172a;">
      <div style="height:6px; background:linear-gradient(90deg,#8b5cf6,#6366f1,#22d3ee);"></div>
      <h2 style="margin:16px 0 8px 0; font-size:18px;">提醒：{name} 即将到来</h2>
      <p style="margin:0 0 12px 0; color:#475569;">日期：{date}</p>
      <a href="{base}" style="display:inline-block; padding:10px 16px; background:#6366f1; color:#fff; text-decoration:none; border-radius:10px;">查看日程</a>
    </div>
    '''
    return subject, body, html

  def _build_password_reset_email(self, context: dict) -> tuple[str, str, str]:
    link = context.get('reset_link', '#')
    subject = f'{settings.app_name} - 重置密码'
    body = f'点击以下链接重置你的密码（15 分钟内有效）：\n{link}'
    html = f'''
    <div style="font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif; color:#0f172a;">
      <div style="height:6px; background:linear-gradient(90deg,#06b6d4,#8b5cf6,#ec4899);"></div>
      <h2 style="margin:16px 0 8px 0; font-size:18px;">重置密码</h2>
      <p style="margin:0 0 12px 0; color:#475569;">点击下方按钮，并在 15 分钟内完成重置。</p>
      <a href="{link}" style="display:inline-block; padding:10px 16px; background:#0ea5e9; color:#fff; text-decoration:none; border-radius:10px;">重置密码</a>
    </div>
    '''
    return subject, body, html


notification_manager = NotificationManager()
